iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0

今天理應照著昨天的技能延伸繼續做下去,但由於想到要做凍結或緩速的技能,就改生成速度和完善整個生成系統。

修改目標生成程式

首先修改原本的TargetSpawner,加入生成間隔的調整設定。目前設定為每提高一個階級,也就是多一個目標時,就減少0.2秒的生成間隔,最短不會低於0.5秒。
另外,也修改了原本的生成方式。每次生成前會先檢查哪些洞口沒有目標,接著再從空洞口中隨機選擇一個生成,避免同一個洞口已經有目標時,又在同一個位置生成新的殭屍。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TargetSpawner : MonoBehaviour
{
    [Header("生成設定")]
    public Transform[] spawnPoints;

    [Header("目標 Prefabs (按 1~5 滴血順序依序拖入)")]
    public GameObject[] targetPrefabs;

    [Header("時間與加速設定")]
    public float baseSpawnInterval = 1.5f;       // 初始生成間隔
    public float intervalDecreasePerTier = 0.2f; // 每升一階減少的生成間隔時間 (秒)
    public float minSpawnInterval = 0.5f;        

    [Header("殭屍移動與停留時間加速設定")]
    public float speedIncreasePerTier = 0.15f;    // 每升一階移動速度增加 15%
    public float durationDecreasePerTier = 0.1f;  // 每升一階停留時間減少 10%

    private float currentSpawnInterval;

    [Header("解鎖門檻設定")]
    public int scoreForType2 = 1000;
    public int scoreForType3 = 2500;
    public int scoreForType4 = 5000;
    public int scoreForType5 = 8500;

    private void Start()
    {
        if (spawnPoints == null || spawnPoints.Length == 0 || targetPrefabs == null || targetPrefabs.Length == 0)
        {
            Debug.LogError("[TargetSpawner] 請確保已指派 spawnPoints 與 targetPrefabs!");
            return;
        }

        currentSpawnInterval = baseSpawnInterval;
        StartCoroutine(SpawnRoutine());
    }

    private IEnumerator SpawnRoutine()
    {
        while (true)
        {
            UpdateSpawnInterval();
            yield return new WaitForSeconds(currentSpawnInterval);
            SpawnTargetAtAvailableHole();
        }
    }

    private void UpdateSpawnInterval()
    {
        int currentScore = ScoreManager.Instance != null ? ScoreManager.Instance.totalScore : 0;
        int tier = GetCurrentTier(currentScore);

        float calculatedInterval = baseSpawnInterval - (tier * intervalDecreasePerTier);
        currentSpawnInterval = Mathf.Max(calculatedInterval, minSpawnInterval);
    }

    // 計算當前門檻階級 (0~4)
    private int GetCurrentTier(int score)
    {
        if (score >= scoreForType5) return 4;
        if (score >= scoreForType4) return 3;
        if (score >= scoreForType3) return 2;
        if (score >= scoreForType2) return 1;
        return 0;
    }

    private void SpawnTargetAtAvailableHole()
    {
        // 1. 找出所有空洞口
        List<Transform> emptyHoles = new List<Transform>();
        foreach (Transform hole in spawnPoints)
        {
            if (hole.childCount == 0)
            {
                emptyHoles.Add(hole);
            }
        }

        if (emptyHoles.Count == 0) return;

        // 2. 取得當前階級與解鎖的殭屍種類
        int currentScore = ScoreManager.Instance != null ? ScoreManager.Instance.totalScore : 0;
        int tier = GetCurrentTier(currentScore);
        int maxUnlockedIndex = Mathf.Min(tier, targetPrefabs.Length - 1);

        // 3. 隨機選取殭屍 Prefab
        int randomTargetIndex = Random.Range(0, maxUnlockedIndex + 1);
        GameObject selectedTargetPrefab = targetPrefabs[randomTargetIndex];

        // 4. 隨機選擇空洞口並生成
        int randomHoleIndex = Random.Range(0, emptyHoles.Count);
        Transform targetPoint = emptyHoles[randomHoleIndex];

        GameObject newTarget = Instantiate(selectedTargetPrefab, targetPoint.position, Quaternion.identity, targetPoint);

        // 5. 設定該隻殭屍的速度與停留時間加成
        Target targetScript = newTarget.GetComponent<Target>();
        if (targetScript != null)
        {
            float speedMultiplier = 1.0f + (tier * speedIncreasePerTier);        // 例如第 2 階為 1.3 倍速
            float durationMultiplier = Mathf.Max(0.3f, 1.0f - (tier * durationDecreasePerTier)); // 停留時間最短為 30%

            targetScript.SetSpeedAndDuration(speedMultiplier, durationMultiplier);
        }
    }
}

這樣一來,生成器就會根據玩家目前的分數計算階級,再依照階級調整生成間隔與可以生成的殭屍種類。

加快目標的出來和縮回去的速度

除了生成間隔之外,這次也讓目標的移動速度與停留時間會隨著階級改變。每提高一個階級,移動速度就會增加15%,頂端停留時間則會減少10%,最短會保留原本停留時間的30%。

為了讓生成器能夠調整這些數值,我在Target中新增了SetSpeedAndDuration(),讓生成器在生成殭屍後,可以依照目前的階級設定移動速度與停留時間。
由於伸出與縮回都使用同一個speed,所以兩個動作也會一起加快。

using System.Collections;
using UnityEngine;

public class Target : MonoBehaviour
{
    [Header("目標基本屬性")]
    public int maxHp = 1;              
    protected int currentHp;           

    [Header("分數設定")]
    public int scoreValue = 50;        

    [Header("移動與時間設定")]
    public float riseDistance = 1.2f;  
    public float speed = 5f;           // 預設移動速度(包含伸出與縮回)
    public float showDuration = 1.0f;  // 頂端停留時間

    private Vector3 startPos;
    private Vector3 targetPos;
    private bool isDefeated = false;   
    private Coroutine targetRoutine;   

    // 動態根據階級調整速度與停留時間
    public void SetSpeedAndDuration(float speedMultiplier, float durationMultiplier)
    {
        speed *= speedMultiplier;          // 速度變快(乘以倍率,例如 1.2、1.4...)
        showDuration *= durationMultiplier; // 停留時間變短(乘以倍率,例如 0.9、0.8...)
    }

    private void Start()
    {
        currentHp = maxHp;
        startPos = transform.position;
        targetPos = startPos + new Vector3(0, riseDistance, 0);

        targetRoutine = StartCoroutine(TargetRoutine());
    }

    private IEnumerator TargetRoutine()
    {
        // 伸出洞口
        while (Vector3.Distance(transform.position, targetPos) > 0.01f)
        {
            transform.position = Vector3.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
            yield return null;
        }
        transform.position = targetPos;

        // 在頂端停留
        yield return new WaitForSeconds(showDuration);

        // 縮回洞口(這裡使用的也是 speed,所以速度會同步提升!)
        while (Vector3.Distance(transform.position, startPos) > 0.01f)
        {
            transform.position = Vector3.MoveTowards(transform.position, startPos, speed * Time.deltaTime);
            yield return null;
        }
        transform.position = startPos;

        if (HealthManager.Instance != null)
        {
            HealthManager.Instance.TakeDamage();
        }

        Destroy(gameObject);
    }

    public virtual void OnHit()
    {
        if (isDefeated) return;

        currentHp--;

        if (currentHp <= 0)
        {
            isDefeated = true;

            if (targetRoutine != null)
            {
                StopCoroutine(targetRoutine);
            }

            if (ScoreManager.Instance != null)
            {
                ScoreManager.Instance.AddScore(scoreValue);
            }

            Destroy(gameObject);
        }
    }
}

第二十五天的成果與之後目標

今天主要完成了以下幾個部分:

  • 生成間隔調整:隨著分數提高,殭屍生成得更頻繁。
  • 避免洞口重複生成:從目前沒有目標的洞口中選擇生成位置。
  • 移動速度提升:殭屍伸出與縮回的速度會隨階級提高。
  • 縮短停留時間:減少玩家可以攻擊殭屍的時間,增加遊戲難度。
    目前遊戲已經能依照分數逐步解鎖不同血量的殭屍,也會提高生成頻率與移動速度。
    接下來會繼續製作技能的實際效果,讓玩家除了擊殺殭屍累積分數之外,也能運用技能來應對逐漸提高的難度。

上一篇
Day 24|製作技能綁定擊殺數系統
下一篇
Day 26|製作全場清除技能
系列文
從基礎到實作:使用 Google AI 學習程式設計 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言